﻿using Microsoft.AspNet.Identity;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Hosting;
using System.Web.Http;
using System.Web.Http.Description;
using YlisChat.DAO;
using YlisChat.Models;

namespace YlisChat.Controllers
{
    [Authorize]
    public class ChatController : BaseController
    {
        /// <summary>
        /// Get the latest 100 chat messages
        /// GET: api/Chat/name
        /// </summary>
        /// <param name="id">The channel name including hashtag</param>
        /// <returns>The last 100 messages</returns>
        [ResponseType(typeof(List<Discussion>))]
        [HttpGet]
        public async Task<IHttpActionResult> Get(string id)
        {
            if (id == null)
                return this.BadRequest("No channel name");

            string userId = User.Identity.GetUserId();

            List<Discussion> Discussions = await (from x in db.Discussion
                                                  where x.HashTag == id
                                                  orderby x.TimeStamp descending
                                                  select x).Take(100).ToListAsync();

            UserList ListUser = await (from x in db.UserList
                                       where x.HashTag == id
                                       where x.UserId == userId
                                       select x).FirstOrDefaultAsync();


            if (ListUser == default(UserList))
                return this.BadRequest("Invalid Channel");

            // Try to find the lates system messasge where userId is current user, this is the join message
            int joinIndex = Discussions.FindIndex(x => x.SystemMessage == true && x.UserId == userId);
            if (joinIndex >= 0)
                Discussions = Discussions.GetRange(0, joinIndex);

            Discussions.Reverse();
            return this.Ok(Discussions);
        }

        /// <summary>
        /// Post a message to a channel.
        /// POST: api/Chat
        /// </summary>
        /// <param name="message"></param>
        [HttpPost]
        public async Task<IHttpActionResult> Post(Discussion message)
        {
            if (message == null)
                return this.BadRequest("No data");

            ApplicationUser user = UserManager.FindById(User.Identity.GetUserId());

            // Chech that the user is in the channel
            UserList luser = await (from x in db.UserList
                                    where x.HashTag == message.HashTag
                                    where x.UserId == user.Id
                                    select x).FirstOrDefaultAsync();

            if (luser == default(UserList))
                return this.BadRequest("Invalid channel");

            message.UserId = user.Id;
            message.TimeStamp = DateTime.UtcNow;
            message.SystemMessage = false;
            message.Id = Guid.NewGuid().ToString();
            message.AttachmentId = String.IsNullOrWhiteSpace(message.AttachmentId) ? null : message.AttachmentId;

            MessageDAO mdao = new MessageDAO(db);
            await mdao.SendMessage(message);
            return this.Ok(1);
        }

        /// <summary>
        /// Join a channel
        /// PUT api/Chat/id
        /// </summary>
        /// <param name="id">The channel name</param>
        /// <returns></returns>
        [ResponseType(typeof(ChannelModel))]
        [HttpPut]
        public async Task<IHttpActionResult> Put(string id)
        {
            if (String.IsNullOrWhiteSpace(id))
                return this.BadRequest("Invalid channel name");

            ChannelModel existing = await (from cha in db.Channels
                                           where cha.HashTag == id
                                           select cha).FirstOrDefaultAsync();
            if (existing == default(ChannelModel))
                return this.BadRequest("Channel not found");

            string userId = User.Identity.GetUserId();

            int isBanned = await (from ban in db.Bans
                                  where ban.UserId == userId
                                  where ban.HashTag == id
                                  select ban).CountAsync();

            if (isBanned > 0)
                return this.StatusCode(HttpStatusCode.Forbidden);

            UserList luser = new UserList()
            {
                UserId = userId,
                HashTag = id,
                AdminState = AdminLevel.USER
            };

            db.UserList.Add(luser);
            await db.SaveChangesAsync();

            // Send the join message
            MessageDAO mdao = new MessageDAO(db);
            mdao.SendSystemMessage(String.Format("@{0} joined {1}", luser.UserId, luser.HashTag), luser.HashTag, luser.UserId);
            return this.Ok(existing);
        }

        /// <summary>
        /// Leave a channel
        /// DELETE api/Chat/id
        /// </summary>
        /// <param name="id">The channel name</param>
        /// <returns></returns>
        [HttpDelete]
        public async Task<IHttpActionResult> Delete(string id)
        {
            if (String.IsNullOrWhiteSpace(id))
                return this.BadRequest("Invalid channel name");

            string userId = User.Identity.GetUserId();
            UserList luser = await (from cha in db.UserList
                                    where cha.HashTag == id
                                    where cha.UserId == userId
                                    select cha).FirstOrDefaultAsync();
            if (luser == default(UserList))
                return this.BadRequest("Channel not found");

            db.UserList.Remove(luser);
            await db.SaveChangesAsync();

            int userCount = await (from cha in db.UserList
                                   where cha.HashTag == id
                                   select cha).CountAsync();

            // Send the leave message
            MessageDAO mdao = new MessageDAO(db);
            mdao.SendSystemMessage(String.Format("@{0} left {1}", luser.UserId, luser.HashTag), luser.HashTag, luser.UserId);

            if(userCount == 0)
                HostingEnvironment.QueueBackgroundWorkItem(ct => DeleteChannel(luser.HashTag));

            return this.Ok(1);
        }

        /// <summary>
        /// Delete the unused channel, also deletes all related messages
        /// </summary>
        /// <param name="hashTag"></param>
        /// <returns></returns>
        private async Task DeleteChannel(string hashTag)
        {
            // Make sure that the channel actually has zero users
            int userCount = await (from cha in db.UserList
                                   where cha.HashTag == hashTag
                                   select cha).CountAsync();

            if (userCount > 0)
                return;

            // Remove messages
            List<Discussion> messages = await (from msg in db.Discussion
                                               where msg.HashTag == hashTag
                                               select msg).ToListAsync();

            if (messages.Count > 0)
                db.Discussion.RemoveRange(messages);

            // Remove channel
            ChannelModel channel = await (from cha in db.Channels
                                          where cha.HashTag == hashTag
                                          select cha).FirstOrDefaultAsync();

            if (channel != default(ChannelModel))
                db.Channels.Remove(channel);

            await db.SaveChangesAsync();
        }
    }
}
